﻿using System;
using System.Collections.Generic;
using System.Text;

namespace AssosiativeMemories.Memories
{
    public enum Transformada {ZERO_HOT, ONE_HOT, ZERO, ONE}

    [Serializable]
    class BAMAlfaBeta : Memory
    {
        /**
         * Linear Associator Modificado formado por los patrones de salida y.
         */
        List<List<int>> LAy;

        /**
         * Memoria Asociativa Alfa-Beta max.
         */
        List<List<int>> max;
        
        /**
         * Memoria Asociativa Alfa-Beta min.
         */
        List<List<int>> min;
        
        public BAMAlfaBeta(int input, int output)
        {
            xSize = input;
            ySize = output;
        }
        public BAMAlfaBeta() { }

        protected void init(int count, List<Asociacion> a)
        {/*Hay que ver que onda con el zero hot y la expansión one hot y contracción. */
            max = new List<List<int>>();
            min = new List<List<int>>();
            for (int i = 0; i < xSize + count; i++) // (One/Zero)-hot
            {
                List<int> plistMax = new List<int>();
                List<int> plistMin = new List<int>();
                for (int j = 0; j < xSize + count; j++) // (One/Zero)-hot
                {
                    plistMax.Add(0);
                    plistMin.Add(2);
                }
                max.Add(plistMax);
                min.Add(plistMin);
            }

            LAy = new List<List<int>>();
            List<int> temp;

            for (int i = 0; i < a[0].y.Count; ++i)
            {
                temp = new List<int>();
                for (int j = 0; j < a.Count; ++j)
                    temp.Add(a[j].y[i]);
                LAy.Add(temp);
            }
        }
        /**
         * Transformada vectorial de expansión.
         * X = tau^e(x,h)
         */
        public List<int> Expansion(List<int> v, int pos, Transformada tipo, int size)
        {
            int type = (tipo == Transformada.ONE_HOT) ? 0 : 1;
            List<int> res = new List<int>();
            foreach (int value in v)
                res.Add(value);

            for (int i = 0; i < size; i++)
                res.Add(type);

            res[pos] = (tipo == Transformada.ONE_HOT) ? 1 : 0;
            return res;
        }
        public List<int> Expansion(List<int> v, Transformada tipo, int size)
        {
            int type = (tipo == Transformada.ONE) ? 1 : 0;
            List<int> res = new List<int>();
            foreach (int value in v)
                res.Add(value);

            for (int i = 0; i < size; i++)
                res.Add(type);

            return res;
        }
        /**
         * Transformada vectorial de contracción.
         * X = tau^e(x,h)
         */
        public List<int> Contraccion(List<int> v, int pos, Transformada tipo, int size)
        {
            List<int> res = new List<int>();
            foreach (int value in v)
                res.Add(value);

            for (int i = 0; i < size; i++)
                res.Add(1);

            res[pos] = 0;

            return res;
        }
        /**
         * Alfa V
         */
        public int[] AlfaMax()
        {
            return null;
        }
        public int[] BetaMax()
        {
            return null;
        }
        public int[] AlfaMin()
        {
            return null;
        }
        public int[] BetaMin()
        {
            return null;
        }
        /**
         * Alpha: AxA -> B
         * -------------------------
         *  x       y     Alpha(x,y)
         * -------------------------
         *  0       0        1
         *  0       1        0
         *  1       0        2
         *  1       1        1
         *  ------------------------
         */
        public int Alfa(int x, int y)
        {
            if (x == y) return 1;
            if (x == 0 && y == 1) return 0;
            return 2;
        }
        /**
         * Alpha: BxA -> A
         * -------------------------
         *  x       y     Beta(x,y)
         * -------------------------
         *  0       0        0
         *  0       1        0
         *  1       0        0
         *  1       1        1
         *  2       0        1
         *  2       1        1
         *  ------------------------
         */
        public int Beta(int x, int y)
        {
            if ((x == 0) || (x == 1 && y == 0)) return 0;
            return 1;
        }

        public override void Learn(List<Asociacion> a)
        {//Hay dos formas, List<List<List<int>>>(es enorme) o algo anidado

            int aux;
            init(a.Count, a);
            /**
             * max
             */
            for (int i = 0; i < max.Count; i++)
                for (int j = 0; j < max[0].Count; j++)
                    for (int k = 0; k < a.Count; k++ )
                    {// Expandir
                        List<int> v1 = Expansion(a[k].x, xSize + k, Transformada.ONE_HOT, a.Count);
                        List<int> v2 = Expansion(a[k].x, xSize + k, Transformada.ONE_HOT, a.Count);
                        //aux = Alfa(asoc.x[i], asoc.x[j]);
                        aux = Alfa(v2[i], v1[j]);
                        if (aux > max[i][j]) max[i][j] = aux;
                        if (aux == 2) break;
                    }
            /* Podemos unir estos dos ciclos. */
            for (int i = 0; i < min.Count; i++)
                for (int j = 0; j < min[0].Count; j++)
                    for(int k=0; k<a.Count; k++)
                    {// Expandir
                        List<int> v1 = Expansion(a[k].x, xSize + k, Transformada.ZERO_HOT, a.Count);
                        List<int> v2 = Expansion(a[k].x, xSize + k, Transformada.ZERO_HOT, a.Count);
                        //aux = Alfa(asoc.x[i], asoc.x[j]);
                        aux = Alfa(v1[i], v2[j]);
                        if (aux < min[i][j]) min[i][j] = aux;
                        if (aux == 0) break;
                    }
        }

        public string hola(List<int> x)
        {
            String str = "";
            foreach(int a in x) str += a + ",";
            return str;
        }

        public void Negar(List<int> b)
        {
            for (int i = 0; i < b.Count; i++) b[i] = (b[i] == 0) ? 1 : 0;
        }

        public List<int> Output(List<int> xt)
        {
            int aux, no = 0;
            List<int> v1 = Expansion(xt, Transformada.ONE, xt.Count);
            List<int> v2 = Expansion(xt, Transformada.ZERO, xt.Count);
            List<int> result = new List<int>();

            int aux1;
            foreach (List<int> m in max)
            {
                aux1 = 2;
                for (int i = 0; i < v1.Count; ++i)
                {
                    aux = Beta(m[i], v1[i]);
                    if (aux < aux1) aux1 = aux;
                    if (aux == 0) break;
                }
                result.Add(aux1);
            }

            List<int> a = new List<int>();
            List<int> c = new List<int>();
            List<int> b = new List<int>();
            for (int i = xt.Count; i < result.Count; i++) a.Add(result[i]);

            for (int i = 0; i < a.Count; i++) if (a[i] == 1) no++;
            if (no > 1)
            {
                List<int> result2 = new List<int>();
                foreach (List<int> m in min)
                {
                    aux1 = 0;
                    for (int i = 0; i < v2.Count; ++i)
                    {
                        aux = Beta(m[i], v2[i]);
                        if (aux > aux1) aux1 = aux;
                        if (aux == 2) break;
                    }
                    result2.Add(aux1);
                }
                for (int i = xt.Count; i < result2.Count; i++) b.Add(result2[i]);
                for (int i = no = 0; i < b.Count; i++) if (b[i] == 0) no++;

                Negar(b);
                if (no == 1) return Memory.MatrixperVector(LAy, b);
            }
            else return Memory.MatrixperVector(LAy, a);

            for (int i = 0; i < b.Count; i++)
            {
                aux = (a[i] == 1 && b[i] == 1 ) ? 1 : 0;
                c.Add(aux);
            }

            return Memory.MatrixperVector(LAy, c);
        }
        public override List<int> Output(Asociacion xp)
        {
            xp.y = Output(xp.x);
            return xp.y;
        }
        public bool equals(List<int> a1, List<int> a2)
        {
            bool esIgual = true;
            if (a1.Equals(a2)) return true;
            if (a1.Count != a2.Count) return false;
            for (int i = 0; i < a1.Count; i++)
            {
                esIgual = a1[i] == a2[i];
                if (!esIgual) break;
            }
            return esIgual;
        }
        public override string ToString()
        {
            string str = "";
            for (int i = 0; i < max.Count; i++)
            {
                for (int j = 0; j < max[0].Count; j++)
                    str += max[i][j] + ",";
                str += "\n";
            }

            str += "\n";
            for (int i = 0; i < min.Count; i++)
            {
                for (int j = 0; j < min[0].Count; j++)
                    str += min[i][j] + ",";
                str += "\n";
            }

            str += "\n";
            for (int i = 0; i < LAy.Count; i++)
            {
                for (int j = 0; j < LAy[0].Count; j++)
                    str += LAy[i][j] + ",";
                str += "\n";
            }
            return str;
        }
    }
}
